/**
 * © 2009 Susanta Tewari
 * This file is part of genomemap.
 *
 * genomemap is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * genomemap is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with genomemap.  If not, see <http://www.gnu.org/licenses/>.
 */
package genomemap.map;

import genomemap.util.AbstractJob;
import genomemap.data.Chromosome;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 *
 * @version 1.0 12/05/2009
 * @author Susanta Tewari
 */
public abstract class ContigMap extends AbstractJob implements Iterable<ContigMap.Contig> {

    /**
     * the probe map for the contig map. A probe map encapsulates the linkage group and a list
     * of probes.
     */
    protected final ProbeMap probeMap;

    /**
     * estimated via the abstract method <code>buildContigMap</code> during instantiation.
     */
    protected List<Contig> contigs;

    protected ContigMap(ProbeMap probeMap) {

        this.probeMap = probeMap;
        contigs = Collections.unmodifiableList(buildContigMap());

    }

    /**
     * gets the gap between this contig and the next one.
     * @param contig
     * @return
     * @throws RuntimeException if contig is the last one on the map
     */
    public Double contigGap(Contig contig) {

        return probeMap.getProbeGap(contig.getEndProbe());
    }

    /**
     * initializes cloneMap and partitionMap
     * @return
     */
    protected abstract List<Contig> buildContigMap();

    /**
     * 
     * @return
     */
    public List<String> getClones() {

        List<String> result = new ArrayList<String>();

        for (Contig contig : contigs) result.addAll(contig.getClones());

        return result;

    }

    /**
     *
     * @return
     */
    public Integer getContigCount() {
        return contigs.size();
    }

    /**
     * Average size (in the same units as the clone size) of a contig in this map.
     * @return Average size of a contig in this map.
     */
    public Double getAvgContigSize() {

        double contigSize = 0;

        for(Contig contig : this) contigSize += contig.getSize();

        double result = contigSize / getContigCount();

        return result;

    }

    /**
     * Average number of clones in a contig of this map.
     * @return average number of clones in a contig of this map
     */
    public Double getAvgClonesPerContig(){

        int cloneCount  = 0;

        for(Contig contig : this) cloneCount += contig.getClones().size();

        double result = ((double) cloneCount) / getContigCount();

        assert result > 1;

        return result;

    }

    // <editor-fold defaultstate="collapsed" desc="Delegation from ProbeMap">
    public Integer getLinkageGroup() {
        return probeMap.getLinkageGroup();
    }

    public List<String> getProbes() {
        return probeMap.getProbes();
    }

    public Double estimateChromosomeSize() {
        return probeMap.estimateChromosomeSize();
    }// </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Overridden Methods">
    @Override
    public Iterator<Contig> iterator() {
        return contigs.iterator();
    }

    @Override
    public String toString() {

        Chromosome chromosome = Chromosome.getInstance(probeMap.getLinkageGroup());

        Set<String> partitionMap = new HashSet<String>();
        for (Contig contig : contigs) partitionMap.addAll(contig.getProbes());

        List<String> probes = probeMap.getProbes();

        StringBuilder strBuilder = new StringBuilder((partitionMap.size() + probes.size()) + 20);

        String rowSeparator = "";
        for (int i = 0; i < (partitionMap.size() + probes.size()) + 11; i++) rowSeparator += "-";
        rowSeparator += "\n";

        for (Iterator<Contig> itr = contigs.iterator(); itr.hasNext();) {

            Contig contig = itr.next();

            for (String clone : contig.getClones()) {

                if (chromosome.isProbe(clone)) strBuilder.append("*");
                else strBuilder.append(" ");

                strBuilder.append(clone + "   ");

                for (int j = 0; j < probes.size(); j++) {

                    if (partitionMap.contains(probes.get(j))) strBuilder.append("|");

                    if (chromosome.isHybridizing(clone, probes.get(j)))
                        strBuilder.append("1");
                    else strBuilder.append(".");



                }

                Iterator<String> geneItr = chromosome.getGenesTagged(clone).iterator();
                while (geneItr.hasNext()) {
                    strBuilder.append(geneItr.next());
                    if (itr.hasNext()) strBuilder.append(", ");
                }

                strBuilder.append("\n");

            }

            if (itr.hasNext()) strBuilder.append(rowSeparator);

        }

        return strBuilder.toString();
    }// </editor-fold>

    /**
     *   ----    -----    ----- (probes)
     *      ------   ------  (connecting clones)
     * <p>
     * In shotgun DNA sequencing projects, a contig (from contiguous) is a set of overlapping DNA
     * segments derived from a single genetic source. A contig in this sense can be used to deduce the
     * original DNA sequence of the source.
     *
     * <p>
     * A contig map depicts the relative order of a linked library of contigs representing a complete
     * chromosome segment.
     * Staden, R. "A strategy of DNA sequencing employing computer programs", Nucleic Acids Res. 7,
     * 2601-2610, (1979). [1]
     * <a href="http://www.pubmedcentral.nih.gov/articlerender.fcgi?artid=327874">Link</a>
     * Source: <a href="http://en.wikipedia.org/wiki/Contig">Wiki:Contig</a>
     *
     * @version 1.0 12/03/2009
     * @author Susanta Tewari
     */
    public class Contig {

        /**
         * probes in the contig i.e. probes in clones
         */
        private List<String> probes = new ArrayList<String>();

        /**
         * All the contiguous clones (including probes) in the contig
         */
        private List<String> clones = new ArrayList<String>();

        void addProbe(String probe) {
            clones.add(probe);
            probes.add(probe);
        }

        void addClone(String clone) {
            clones.add(clone);
        }

        void addClones(List<String> clones) {
            this.clones.addAll(clones);
        }

        String getStartProbe() {
            return probes.get(0);
        }

        String getEndProbe() {
            return probes.get(probes.size() - 1);
        }

        Double getSize() {
            return probeMap.estimateContigSize(this);
        }

        /**
         * Returns the clones in the contig. This includes the probes as well.
         * @return clones in the contig
         */
        public List<String> getClones() {
            return clones;
        }

        /**
         * Returns the probes in the contig
         * @return probes in the contig
         */
        List<String> getProbes() {
            return probes;
        }

    }
}




